/*
 * Funambol is a mobile platform developed by Funambol, Inc. 
 * Copyright (C) 2008 Funambol, Inc.
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission 
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE 
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Affero General Public License 
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 * 
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite 
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 * 
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 * 
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably 
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */


#include "RecurrenceDataStore.h"
#include "EventDataStore.h"
#include "base/Log.h"
#include "constants.h"

static bool convertStringToBool(const char* d) {
    return strcmp(d, "0") == 0 ? false : true;
}

static const char* getElementOfArray(const ArrayList& al, int index) {        
    StringBuffer* ret = (StringBuffer*)al.get(index);
    LOG.debug("getElementOfArray = %s", ret->c_str());   
    return ret->c_str();    
}

static long getIntElementOfArray(const ArrayList& al, int index) {
    StringBuffer* ret = (StringBuffer*)al.get(index);
    LOG.debug("getIntElementOfArray = %s", ret->c_str());
    long result = atol(ret->c_str());
    return result;   
}

/****
*****
***** Query for the insert Recurrence. The event has to be inserted in several tables
*****
*****/
StringBuffer RecurrenceDataStore::createInsertRecurrenceStmt(const char* id_event, Recurrence& rec) {    
    
    StringBuffer query("INSERT INTO ");
    query.append(TABLE_RECURRENCE);
    query.append(" (");    
    // list of fields
    query.append("frequency, interval, week_start, count, cached_end_date, ");
    query.append("end_date, by_month_months, event_id");
        
    // list of values
    query.append(") values (");    
    
    query.append(rec.getFrequency()).append(",");
    query.append(rec.getInterval()).append(",");
    query.append(rec.getWeek_start()).append(",");            
    query.append("0").append(",");
    query.append("NULL").append(",");
    
    // it checks only the string part... @todo: to be reviewed
    Date empty;
    Date& d = rec.getEndDate();
    StringBuffer sb("0");
    if (d != empty) {
        sb = convertStandardToAppleDate(d);
    }
    
    query.append(sb.c_str()).append(",");
    query.append("0").append(",");        
    query.append(id_event);
          
    query.append(");");
    LOG.debug("**RecurrenceDataStore:createInsertRecurrenceStmt: %s\n", query.c_str()); 
    return query;          
}

StringBuffer RecurrenceDataStore::createInsertRecurrenceChangesStmt(const char* rec_id, const char* event_id, Event& event) {    
     
    StringBuffer query("INSERT INTO ");
    query.append(TABLE_RECURRENCE_CHANGES);
    query.append(" (");    
    // list of fields
    query.append("record, type, external_id, store_id, event_id_tomb, calendar_id");
        
    // list of values
    query.append(") values (");    
    
    query.append(rec_id).append(",");
    query.append("0").append(",");
    query.append("NULL").append(",");
    query.append("1").append(",");                   
    query.append(event_id).append(",");
    query.append(event.getCalendar());
    query.append(");");
    LOG.debug("**EventDataStore:createInsertRecurrenceChangesStmt: %s\n", query.c_str()); 
    return query;          
}

StringBuffer RecurrenceDataStore::createInsertOccurrenceCacheStmt(const char* event_id, 
                                        const char* day, 
                                        const char* occurrenceDate, 
                                        Event& event) {    
    
    StringBuffer query("INSERT INTO ");
    query.append(TABLE_OCCURRENCE_CACHE);
    query.append(" (");    
    // list of fields
    query.append("day, event_id, calendar_id, store_id, occurrence_date");
        
    // list of values
    query.append(") values (");        
    query.append(day).append(",");    
    query.append(event_id).append(",");
    query.append(event.getCalendar()).append(",");
    query.append("1").append(",");    
    query.append(occurrenceDate);    
    query.append(");");
    LOG.debug("**RecurrenceDataStore:createInsertOccurrenceCacheStmt: %s\n", query.c_str()); 
    return query;  

}

/*
 * From server -> client. Occurrences to be calculated 
 */
int RecurrenceDataStore::calculateOccurrencesDates(Event& event) { 
    
    if ((Recurrence*)event.getRecurrence() == NULL) {
        return -1;
    } 
    
    Recurrence& recurrence = (*(Recurrence*)event.getRecurrence());
       
    Frequency freq  = recurrence.getFrequency();
    
    int days        = 1;
    //long previousYear = 0;
    int interval = recurrence.getInterval();
    
    // the start date of the event
    long date = getAppleDateValueFromStandard(event.getStart());
    Timezone& eventTz = (Timezone&)event.getStart().getTimezone();

    // it is used because 
    Timezone currentTz(getCurrentTimezone()); 
    
    // It can be 0. If it is with end date it means that the patternEndDate must be used
    // otherwise it is a noenddate event
    int occurrences = recurrence.getOccurrences();
    
    if (recurrence.getNoEndDate() == true) {
        occurrences = MAX_NUMBER_OCCUR;
    } else {
        if (occurrences == 0) {
            // to be calculated from the patternEndDate
            StringBuffer patternEndDates = convertStandardToAppleDate(recurrence.getPatternEndDate());
            long patternEndDate = atol(patternEndDates);
            switch (freq) {
                case daily: {
                        occurrences = getNumberOfDaysByDates(date, patternEndDate, eventTz);
                        occurrences = occurrences/interval;
                        occurrences++; // add 1 due to the calculation algo of daily 
                    }
                    break;
                case weekly: {
                        occurrences = getNumberOfDaysByDates(date, patternEndDate, eventTz);
                        occurrences = occurrences/(interval * 7);
                        occurrences++; // add 1 due to the calculation algo of daily 
                    }
                    break;
                case monthly: {
                        long months = getNumberOfMonthsByDates(date, patternEndDate, eventTz);
                        occurrences = months/interval;
                    }
                    break;
                case yearly: {
                        long months = getNumberOfMonthsByDates(date, patternEndDate, eventTz);
                        occurrences = months/(interval * 12);
                        occurrences++; // add 1 due to the calculation algo of daily 
                    }
                    break;
                default:
                    LOG.debug("RecurrenceDataStore::calculateOccurrencesDates. Frequency not supported...");                    
                    break;
            }
                
        }
    }
    
    switch (freq) {
            
        case daily: 
        case weekly: 
        {
            if (freq == weekly) {
                days = 7; 
            }
            if (recurrence.getNoEndDate() == false) {
                
                // calculate the end recurrence date
                long endDayRec = addDaysToDate(date, eventTz, (interval *  days * (occurrences-1)));
                endDayRec = getDateEndRecurrence(endDayRec, eventTz);
                StringBuffer b = convertAppleDateToStandard(endDayRec, false, true); // in utc. to change for timezone
                Date d; d.setDate(b.c_str());
                recurrence.setEndDate(d);
            } else {
                // recurrenceEndDate is not touched so it is always set to default. No end date
                // in the db it must be set to 0
            }
            for (int i = 0; i < MAX_NUMBER; i++) { 
                if (i < occurrences) {
                    long midnight = getLocalMidnightAppleDate(date, currentTz);
                    StringBuffer s; s.append(date);
                    StringBuffer sm; sm.append(midnight);
                    recurrence.getOccurrence_date().add(s);
                    recurrence.getDay().add(sm);
                    
                    date = addDaysToDate(date, eventTz, (interval *  days));
                }
                else {
                    break;
                }
            }                
            
            break;
        }
        case (monthly): {
            int i = 0;
            for (i = 0; i < MAX_NUMBER; i++) { 
                if (i < occurrences) {
                    long midnight = getLocalMidnightAppleDate(date, currentTz);
                    StringBuffer s;  s.append (date);
                    StringBuffer sm; sm.append(midnight);
                    recurrence.getOccurrence_date().add(s);
                    recurrence.getDay().add(sm);
                    if (occurrences != i+1) {
                        date = addMonthsToDate(date, eventTz, interval);
                    }
                }
                else {
                    break;
                }
            }   
            if (recurrence.getNoEndDate() == false) {
                for (i; i < occurrences; i++) { //to get the remaining part of the occurrences until the end to get the latest date                    
                    if (occurrences != i+1) {
                        date = addMonthsToDate(date, eventTz, interval);
                    }
                }  
                // calculate the end recurrence date
                long endDayRec = getDateEndRecurrence(date, eventTz);
                StringBuffer b = convertAppleDateToStandard(endDayRec, false, true);
                Date d; d.setDate(b.c_str());
                recurrence.setEndDate(d);
            }        
            break;
        } 
        case (yearly): {   
            int i = 0;
            for (i = 0; i < MAX_NUMBER; i++) { //40 is the maximum number of items that we want to set in the OccurrenceCache
    
                if (i < occurrences) {
                    long midnight = getLocalMidnightAppleDate(date, currentTz);
                    StringBuffer s;  s.append (date);
                    StringBuffer sm; sm.append(midnight);
                    recurrence.getOccurrence_date().add(s);
                    recurrence.getDay().add(sm);
                    if (occurrences != i+1) {
                        date = addYearsToDate(date, eventTz, interval);
                    }
                }
                else {
                    break;
                }
            }   
            if (recurrence.getNoEndDate() == false) {
                for (i; i < occurrences; i++) { //to get the remaining part of the occurrences until the end to get the latest date
                    if (occurrences != i+1) {
                        date = addYearsToDate(date, eventTz, interval);
                    }
                }  
                // calculate the end recurrence date
                long endDayRec = getDateEndRecurrence(date, eventTz);
                StringBuffer b = convertAppleDateToStandard(endDayRec, false, true);
                Date d; d.setDate(b.c_str());
                recurrence.setEndDate(d);
                
            }
            break;
            
        }
        default: {
            LOG.debug("Recurrence not supported %i", freq);
            break;
        }
    }
    return 0;
}  


int RecurrenceDataStore::insertRecurrence(Event& event, const char* id_event) {
    int ret = 0;
    ArrayList al;
       
    calculateOccurrencesDates(event);
    
    Recurrence* rec = (Recurrence*)event.getRecurrence();
    StringBuffer rec_id;

    //
    // day and occurrDay have the same number of element
    //
    ArrayList& day       = rec->getDay();
    ArrayList& occurrDay = rec->getOccurrence_date();
    
                    
    // Recurrence        
    StringBuffer query = createInsertRecurrenceStmt(id_event, *rec);    
    ret = driver.execute(query);
    
    if (ret != 0){
        LOG.error("RecurrenceDataStore.insertRecurrence: the item %s can not be inserted: err = %i", event.getSubject(), ret);        
        goto finally;
    }
    ret = driver.commit();
    if (ret != 0){
        LOG.error("RecurrenceDataStore.insertRecurrence: error commit: err = %i", ret);                
        goto finally;
    }
    
    // get the id of the Recurrence just inserted
    ret = driver.queryIndex(QUERY_MAX_ID_FROM_RECURRENCE, al);
    if (ret != 0){
        LOG.error("RecurrenceDataStore.insertRecurrence: error queryIndex: err = %i", ret);                
        goto finally;        
    }
    rec_id = ((StringBuffer*)(al.get(0)))->c_str();
    
    // RecurrenceChanges table
    query = createInsertRecurrenceChangesStmt(rec_id, id_event, event);
    ret = driver.execute(query);    
    if (ret != 0){
        LOG.error("RecurrenceDataStore.insertRecurrence: error createInsertRecurrenceChangesStmt: err = %i", ret);                
        goto finally;
    }
    ret = driver.commit();
    if (ret != 0){
        LOG.error("RecurrenceDataStore.insertRecurrence: error commit: err = %i", ret);
        goto finally;
    }
               
    for (int i = 0; i < day.size(); i++) {
        StringBuffer* dd  = (StringBuffer*)day.get(i);
        StringBuffer* occ = (StringBuffer*)occurrDay.get(i);
        query = createInsertOccurrenceCacheStmt(id_event, dd->c_str(), occ->c_str(), event);
        ret = driver.execute(query);    
        if (ret != 0){
            LOG.error("RecurrenceDataStore.createInsertOccurrenceCacheStmt: error createInsertOccurrenceCacheStmt: err = %i", ret);                
            goto finally;
        }
        ret = driver.commit();
        if (ret != 0){
            LOG.error("RecurrenceDataStore.createInsertOccurrenceCacheStmt: error commit: err = %i", ret);
            goto finally;
        }    
    }              
       
finally:      
    return ret;
}


StringBuffer RecurrenceDataStore::createDeleteRecurrenceChangesStmt(const char* rec_id, 
                                                                    const char* event_id, 
                                                                    const char* calendar_id) {
    
    StringBuffer query("INSERT INTO ");
    query.append(TABLE_RECURRENCE_CHANGES);
    query.append(" (");    
    // list of fields
    query.append("record, type, external_id, store_id, event_id_tomb, calendar_id");
        
    // list of values
    query.append(") values (");    
    
    query.append(rec_id).append(",");
    query.append("2").append(",");
    query.append("NULL").append(",");
    query.append("1");                   
    query.append(event_id).append(",");
    query.append(calendar_id).append(",");
    query.append(");");
    LOG.debug("**EventDataStore:createDeleteRecurrenceChangesStmt: %s\n", query.c_str()); 
    return query;       
}

StringBuffer RecurrenceDataStore::createDeleteRecurrenceStmt(const char* id) {
    
    StringBuffer query("DELETE FROM ");
    query.append(TABLE_RECURRENCE);
    query.append(" WHERE event_id = ");
    query.append(id);
    query.append(";");
    LOG.debug("**EventDataStore:createDeleteRecurrenceStmt: %s\n", query.c_str()); 
    return query;
}

int RecurrenceDataStore::deleteRecurrence(const char* event_id) {
    
    int ret = 0;
    ArrayList al;
    StringBuffer rec_id;
    StringBuffer calendar_id;
        
    // get the id of the Recurrence just inserted
    StringBuffer query = QUERY_ROW_ID_RECURRENCE;    
    query.append(event_id).append(";");
    
    ret = driver.queryIndex(query, al);
    if (ret != 0){
        LOG.error("RecurrenceDataStore.deleteRecurrence: error queryIndex: err = %i", ret);                
        goto finally;        
    }
    rec_id = ((StringBuffer*)(al.get(0)))->c_str();
        
    al.clear();
    
    query = QUERY_CALENDAR_ID_FROM_RECURRENCE;
    
    query.append(event_id).append(";");
    ret = driver.queryIndex(query, al);
    if (ret != 0){
        LOG.error("RecurrenceDataStore.deleteRecurrence: error queryIndex: err = %i", ret);                
        goto finally;        
    }
    calendar_id = ((StringBuffer*)(al.get(0)))->c_str();
    
    // RecurrenceChanges          
    query = createDeleteRecurrenceChangesStmt(rec_id, event_id, calendar_id);
    ret = driver.execute(query);
    
    if (ret != 0){
        LOG.error("RecurrenceDataStore.createDeleteRecurrenceChangesStmt: the item %s can not be inserted: err = %i", event_id, ret);        
        goto finally;
    }
    
    ret = driver.commit();
    if (ret != 0){
        LOG.error("RecurrenceDataStore.insertRecurrence: error commit: err = %i", ret);                
        goto finally;
    }
    
    // Recurrence        
    query = createDeleteRecurrenceStmt(event_id);    
    ret = driver.execute(query);
    
    if (ret != 0){
        LOG.error("RecurrenceDataStore.insertRecurrence: the item %s can not be inserted: err = %i", event_id, ret);        
        goto finally;
    }
    
    ret = driver.commit();
    if (ret != 0){
        LOG.error("RecurrenceDataStore.insertRecurrence: error commit: err = %i", ret);                
        goto finally;
    }
    
finally:                    
    return ret;
}


int RecurrenceDataStore::updateRecurrence(Event& item, const char* id_event) {

    int ret = 0;    
    return ret;
}
   
bool RecurrenceDataStore::containsRecurrence(const char* identifier, ArrayList& al) {
    
    StringBuffer query("select rowid from Recurrence where event_id = ");
    query.append(identifier).append(";");
    bool ret = false;
    
    int result = driver.queryIndex(query, al);
    if (result != 0){
        LOG.error("RecurrenceDataStore.containsRecurrence: error queryIndex: err = %i", result);
        goto finally;        
    }
    
    if (al.size() > 0) {
        ret = true;
    }
    
finally:
    return ret;
}

Recurrence* RecurrenceDataStore::getRecurrence(Event& event, const char* id_event) { 
    
    ArrayList al;
    int ret = 0;
    
    Recurrence* recurrence = NULL;
    if (!containsRecurrence(id_event, al)) {
        LOG.debug("There are no Recurrence for the event");
        goto finally;
    }
    
    recurrence = new Recurrence();
    for (int i = 0; i < al.size(); i++) {
        ArrayList result, occurrencesCacheItems;
        StringBuffer* rec_id = (StringBuffer*)al.get(i);
        StringBuffer query = "select frequency, interval, week_start, count, cached_end_date, cached_end_date_tz, \
                              end_date, specifier, by_month_months FROM Recurrence WHERE ROWID = ";
        query.append(rec_id);
        query.append(";");
        LOG.debug("RecurrenceDataStore.getRecurrence: the query is %s", query.c_str());
        
        ret = driver.queryColumns(query, result);
        if (ret != 0) {
            LOG.error("getRecurrence error to get the item with id = %s", rec_id->c_str());
            goto finally;
        }
        
        // retrieved all the information from the Recurrence table
        recurrence = populateRecurrence(result);
        if (!recurrence) {
            LOG.debug("The recurrence after popupateRecurrence is NULL!");
        }
        
        // fills the day and occurrence_date arraylist with the values
        // that are in the database
        query = "select day, occurrence_date from OccurrenceCache where event_id = ";
        query.append(id_event).append(";");
        LOG.debug("RecurrenceDataStore.getRecurrence: get number of occurrences %s", query.c_str());
        ret = driver.queryColumns(query, occurrencesCacheItems);
        if (ret != 0) {
            LOG.error("getRecurrence error to get the item with id = %s", rec_id->c_str());
            goto finally;
        }
        
        for (int j = 0; j < occurrencesCacheItems.size(); j++) {
            if (j%2 == 0) {
                recurrence->getDay().add((*(StringBuffer*)occurrencesCacheItems.get(j)));
            } else {
                recurrence->getOccurrence_date().add((*(StringBuffer*)occurrencesCacheItems.get(j)));
            }
        }
                
        // populate all the fields needed....
        getExistingOccurrences(*recurrence, event.getStart());              
       
    }
finally:
    return recurrence;
}

void RecurrenceDataStore::fillRecurringNoEndDateYears(long date, ArrayList& list) {
    
    long adate = getYearFromAppleDate(date);
    for (int i = 0; i < MAX_RRULE_RECORD_NUMBER; i++) {
        StringBuffer y; y.append(adate);
        list.add(y);
        date += 1; 
    }
    return;
}

/*
 * From client -> server. Occurrences already present in the database
 */
int RecurrenceDataStore::getExistingOccurrences(Recurrence& recurrence, Date& start) {
    
    Frequency freq  = recurrence.getFrequency();
    int rec         = 0;
    int days        = 1;
    long previousYear = 0;
    int interval = recurrence.getInterval();
    
    // the start date of the event
    long date = getAppleDateValueFromStandard(start);
    Timezone& currentTz = (Timezone&)start.getTimezone();
    
    switch (freq) {
        
        if (recurrence.getNoEndDate() == true) {
            recurrence.setOccurrences(0);   // means it hasn't to be considered
            fillRecurringNoEndDateYears(date, recurrence.getYearsForTimezoneInfo());
        } 
            
        case daily: 
        case weekly: 
        {
            if (freq == weekly) {
                days = 7; 
                long wd = getDayOfWeekFromAppleDate(date);
                long weekday = convertDayofWeekFromAppleToStandard(wd);
                if (weekday > 0) {
                    recurrence.setDayOfWeek(weekday);
                }
            }
            
            if (recurrence.getNoEndDate() == false) {
                previousYear = getYearFromAppleDate(date);
                StringBuffer s; s.append(previousYear);
                recurrence.getYearsForTimezoneInfo().add(s);
                
                long endRec = getAppleDateValueFromStandard(recurrence.getEndDate());
                int i = 0;
                for (i = 0; i < MAX_OCCURRENCES_ALLOWED; i++) {
                    if (date < endRec) {
                        if (i%20 == 0 && (previousYear != getYearFromAppleDate(date))) {
                            previousYear = getYearFromAppleDate(date);
                            StringBuffer s; s.append(previousYear);
                        }
                        date = addDaysToDate(date, currentTz, (interval *  days));
                    }
                    else
                        break;
                }
                recurrence.setOccurrences(i);
                rec = i;
            }
            break;
        }
        case (monthly): {
            
            long daymonth = getDayOfMonthFromAppleDate(date);
            
            if (daymonth > 0) {
                recurrence.setDayOfMonth(daymonth);
            }
            
            // Iphone treats the monthly event (every 31 of the month only for the month that are of 31
            // days, excluding the ones that are of 30 days. Instead, syncing with outlook and itunes the events
            // are reported every last day of the month 
            if (recurrence.getNoEndDate() == false) {
                long endRec = getAppleDateValueFromStandard(recurrence.getEndDate());
                int i = 0;
                long month = 0, year = 0;
                long endMonth = getMonthOfYearFromAppleDate(endRec);
                long endtYear = getYearFromAppleDate(endRec);
                
                for (i = 0; i < MAX_OCCURRENCES_ALLOWED; i++) {
                    month = getMonthOfYearFromAppleDate(date);
                    year  = getYearFromAppleDate(date);
                    if (month != endMonth || year != endtYear) {
                        date = addMonthsToDate(date, currentTz, interval);
                        StringBuffer s = convertAppleDateToStandard(date, false, false);
                        
                        if (previousYear != year) {
                            previousYear = year;
                            StringBuffer s; s.append(previousYear);
                            recurrence.getYearsForTimezoneInfo().add(s);
                        }
                    }
                    else
                        break;
                }
                recurrence.setOccurrences(i);
                rec = i;
            }
            break;
        } 
        case (yearly): {   
            
            long daymonth = getDayOfMonthFromAppleDate(date);
            long monthyear = getMonthOfYearFromAppleDate(date);
            if (daymonth > 0) {
                recurrence.setDayOfMonth(daymonth);
            }
            if (monthyear) {
                recurrence.setMonthOfYear(monthyear);
            }
            if (recurrence.getNoEndDate() == false) {
                long endRec  = getAppleDateValueFromStandard(recurrence.getEndDate());
                long endyear = getYearFromAppleDate(endRec);
                long year = getYearFromAppleDate(date);
                int i = 0;
                for (i = 0; i < MAX_OCCURRENCES_ALLOWED; i++) {
                    if (year <= endyear) {
                        StringBuffer s; s.append(year);
                        recurrence.getYearsForTimezoneInfo().add(s);
                        year++;
                    }
                    else
                        break;
                }
                recurrence.setOccurrences(i);
                rec = i;
            }
            break;
        }
    }
    
    return rec;
}


/**
 * this is the order to get the fields of the Recurrence table and fill the 
 * Recurrence object to be set in the Event
 "select frequency, interval, week_start, count, cached_end_date, cached_end_date_tz, \
 end_date, specifier, by_month_months FROM Recurrence WHERE ROWID = ";
 */                         
Recurrence* RecurrenceDataStore::populateRecurrence(const ArrayList& al) {
    
    int size = al.size();
    int value = 0;
	LOG.debug("The size of the arrayList in populateRecurrence RecurrenceDataStore %i", size);
    if (size == 0) {
        return NULL;
    }
    
    Recurrence* recurrence = new Recurrence();
        
    for (int i = 0; i < size; i++) {
        value = 0;
        switch(i) {
            case 0: {
                value = getIntElementOfArray(al, 0);
                recurrence->setFrequency((Frequency)value);
                break;
            }
            case 1: {
                recurrence->setInterval(getIntElementOfArray(al, 1));
                break;
            }
            case 2: {
                recurrence->setWeek_start(getIntElementOfArray(al, 2));
                break;
            }
            case 3: {
                recurrence->setCount(getIntElementOfArray(al, 3));
                break;
            }
            case 4: {
                value = getIntElementOfArray(al, 4);
                if (value != 0 && value != NULL) {
                    StringBuffer s = convertAppleDateToStandard(getElementOfArray(al, 4), false, false);
                    Date cached_end_date(s.c_str());
                    recurrence->setCached_end_date(cached_end_date);
                }
                break;
            }
            case 5: {
                recurrence->setCached_end_date_tz(getElementOfArray(al, 5));
                break;
            }
            case 6: {
                // if the end date is 0 then the recurrence has no end date (it doens't finish)
                value = getIntElementOfArray(al, 6);
                if (value != 0 && value != NULL) {
                    StringBuffer s = convertAppleDateToStandard(getElementOfArray(al, 6), false, false);
                    Date endDate(s.c_str());
                    recurrence->setEndDate(endDate);
                    recurrence->setNoEndDate(false);
                } else {
                    recurrence->setNoEndDate(true);
                }
                break;
            }
            case 7: {
                recurrence->setSpecifier(getElementOfArray(al, 7));                
                break;
            }
            case 8: {
                recurrence->setBy_month_months(getIntElementOfArray(al, 8));
                break;
            }
            default:
                LOG.error("Recurrence property not supported");
                break;
        }                    
    }
    
    return recurrence;    
}

